Current File : /home/jeconsul/public_html/wp-content/plugins/suremails/src/screens/connections/providers-drawer.js |
// ProvidersDrawer.js
import { useState, useEffect, useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Drawer, Button, toast } from '@bsf/force-ui';
import {
LoaderCircle as LoaderIcon,
ChevronLeft as ChevronLeftIcon,
} from 'lucide-react';
import ProviderList from '@screens/connections/provider-list';
import { testAndSaveEmailConnection as apiTestAndSaveEmailConnection } from '@api/connections';
import { useMemo } from 'react';
import ProvidersSkeleton from './providers-skeleton';
import ExtendedDynamicForm from './extended-dynamic-form';
const ProvidersDrawer = ( {
isOpen,
setIsOpen,
currentConnection = {},
onSave,
providers: providersList = [],
isProvidersLoading = false,
sequenceId = 1,
connectionCount = {},
} ) => {
const [ selectedProvider, setSelectedProvider ] = useState(
currentConnection ? currentConnection.type : ''
);
const [ formData, setFormData ] = useState( currentConnection );
const [ errors, setErrors ] = useState( {} );
const [ isLoading, setIsLoading ] = useState( false );
// Add form refs
const formRef = useRef( null );
// Get the title postfix for the selected provider
const titlePostfix = ( connectionCount[ selectedProvider ] || 0 ) + 1;
// Get the fields for the selected provider
const selectedProviderData = useMemo( () => {
return providersList.find(
( provider ) => provider.value === selectedProvider
);
}, [ selectedProvider, providersList, currentConnection ] );
const fields = selectedProviderData?.fields;
const defaultValues = useMemo( () => {
return selectedProviderData?.fields.reduce( ( acc, field ) => {
acc[ field.name ] = field.default;
return acc;
}, {} );
}, [ selectedProviderData, currentConnection ] );
useEffect( () => {
if ( currentConnection?.type ) {
setSelectedProvider( currentConnection.type );
setFormData( ( prevData ) => ( {
...prevData,
...currentConnection,
} ) );
} else {
setSelectedProvider( null );
setFormData( null );
}
}, [ currentConnection ] );
useEffect( () => {
if ( ! formData || Object.keys( formData ).length === 0 ) {
const config = selectedProviderData;
if ( ! config ) {
return;
}
// Get the current count for the selected provider and add 1 for priority
const defaultTitle =
titlePostfix === 1
? config.title
: `${ config.title } (${ titlePostfix - 1 })`;
setFormData( {
...defaultValues,
connection_title: defaultTitle,
priority: sequenceId,
} );
}
}, [ selectedProvider, formData, currentConnection ] );
const handleProviderSelect = ( provider ) => {
setSelectedProvider( provider );
const config = selectedProviderData;
if ( ! config ) {
return;
}
// Get the current count for the selected provider and add 1 for priority
const defaultTitle =
titlePostfix === 1
? config.title
: `${ config.title } (${ titlePostfix - 1 })`;
setFormData( {
...defaultValues,
connection_title: defaultTitle,
priority: sequenceId,
} );
setErrors( {} );
};
// On blur, validate the input.
const handleOnBlurValidation = ( event ) => {
if ( ! event.target ) {
return;
}
const field = event.target.name;
const config = selectedProviderData;
if ( ! config ) {
return;
}
const { schema } = config;
try {
schema.pick( { [ field ]: true } ).parse( {
[ field ]: formData[ field ],
} );
setErrors( ( prev ) => ( {
...prev,
[ field ]: undefined,
} ) );
} catch ( error ) {
setErrors( ( prev ) => ( {
...prev,
[ field ]: error.errors[ 0 ].message,
} ) );
}
};
const handleSetOpenDrawer = ( value ) => {
setIsOpen( value );
if ( ! value ) {
setSelectedProvider( '' );
setErrors( {} );
setFormData( {} );
}
};
const validateForm = () => {
const config = selectedProviderData;
if ( ! config ) {
return false;
}
const { schema } = config;
try {
schema.parse( formData );
setErrors( {} );
return true;
} catch ( error ) {
const formattedErrors = {};
error.errors.forEach( ( err ) => {
formattedErrors[ err.path[ 0 ] ] = err.message;
} );
setErrors( formattedErrors );
// Focus the first input with error
const firstErrorField = error.errors[ 0 ]?.path[ 0 ];
const firstErrorInput = formRef.current?.querySelector(
`input[name="${ firstErrorField }"]`
);
firstErrorInput?.focus();
return false;
}
};
const resetProviderState = () => {
setSelectedProvider( null );
setFormData( null );
setErrors( {} );
};
const hasChanges =
JSON.stringify( formData ) !== JSON.stringify( currentConnection ) ||
formData?.force_save === true;
const handleSaveChanges = async () => {
if ( ! hasChanges ) {
toast.info( __( 'No changes to save.', 'suremails' ) );
return;
}
if ( ! validateForm() ) {
return;
}
const payload = {
settings: formData,
provider: selectedProvider.toUpperCase(),
};
setIsLoading( true );
try {
const response = await apiTestAndSaveEmailConnection( payload );
if ( response?.success ) {
toast.success( __( 'Verification successful!', 'suremails' ), {
description: __(
'Connection tested and saved successfully!',
'suremails'
),
} );
setIsOpen( false ); // Close drawer on success
onSave( response.connection );
resetProviderState();
} else {
toast.error( __( 'Verification Failed!', 'suremails' ), {
description: response.message,
autoDismiss: false,
} );
}
} catch ( error ) {
toast.error( __( 'Verification Failed!', 'suremails' ), {
description:
error.message ||
__(
'An unexpected error occurred while testing the connection.',
'suremails'
),
autoDismiss: false,
} );
} finally {
setIsLoading( false );
}
};
/**
* Captures form data from provider-specific forms.
*
* @param {Object} data - The form data.
*/
const handleFormSubmit = ( data ) => {
const [ field, value ] = Object.entries( data )[ 0 ];
setFormData( ( prev ) => ( {
...prev,
[ field ]: value,
} ) );
// Clear error only for the field being changed
setErrors( ( prev ) => ( {
...prev,
[ field ]: undefined,
} ) );
};
// Define drawer title and description based on selected provider
const title = selectedProvider
? __( 'Connection Details', 'suremails' )
: __( 'New Connection', 'suremails' );
const description = selectedProvider
? selectedProviderData?.description ??
__(
'Enter the details below to connect with your {providerName} account.',
'suremails'
).replace(
'{providerName}',
selectedProviderData?.display_name || selectedProvider
)
: __(
'Pick an email provider to ensure your WordPress emails are delivered securely and reliably.',
'suremails'
);
return (
<Drawer
design="footer-bordered"
exitOnEsc
position="right"
scrollLock
transitionDuration={ 0.2 }
open={ isOpen }
setOpen={ handleSetOpenDrawer }
className="z-999999"
>
<Drawer.Backdrop />
<form ref={ formRef } noValidate>
<Drawer.Panel className="w-[34.75rem]">
<Drawer.Header>
<div className="flex items-center justify-between text-text-primary">
<Drawer.Title>{ title }</Drawer.Title>
<Drawer.CloseButton
type="button"
onClick={ () => {
resetProviderState();
setIsOpen( false );
} }
/>
</div>
<Drawer.Description className="text-sm font-normal text-text-secondary">
{ description }
</Drawer.Description>
</Drawer.Header>
<Drawer.Body className="overflow-x-hidden">
{ /* Form when a provider is selected */ }
{ ! isProvidersLoading && selectedProvider && (
<div>
<ExtendedDynamicForm
fields={ fields }
onChange={ handleFormSubmit }
connectionData={ formData }
errors={ errors }
inlineValidator={ handleOnBlurValidation }
/>
</div>
) }
{ /* Provider List when no provider is selected */ }
{ ! isProvidersLoading && ! selectedProvider && (
<ProviderList
onSelectProvider={ handleProviderSelect }
providers={ providersList }
/>
) }
{ /* Skeleton for loading state */ }
{ isProvidersLoading && <ProvidersSkeleton /> }
</Drawer.Body>
{ selectedProvider && (
<Drawer.Footer>
<Button
onClick={ () => {
setSelectedProvider( null );
setFormData( null );
setErrors( {} );
} }
variant="outline"
icon={ <ChevronLeftIcon /> }
size="sm"
iconPosition="left"
className="font-medium"
type="button"
>
{ __( 'Back', 'suremails' ) }
</Button>
<Button
variant="primary"
loading={ isLoading }
icon={
isLoading ? (
<LoaderIcon className="animate-spin" />
) : null
}
onClick={ handleSaveChanges }
className="font-medium"
size="sm"
type="button"
>
{ isLoading
? __( 'Testing…', 'suremails' )
: __( 'Save Changes', 'suremails' ) }
</Button>
</Drawer.Footer>
) }
</Drawer.Panel>
</form>
</Drawer>
);
};
export default ProvidersDrawer;